//MPI c++ wrapper
//$Id: easy_mpi.h 667 2011-02-26 18:59:32Z Oleg.Bulychov $
#pragma once

#include <map>
#if !defined(MPICH_SKIP_MPICXX)
#define MPICH_SKIP_MPICXX
#endif
#include <mpi.h>
#include "src/heo/include/reflection.h"
#include "src/heo/include/transmit.h"

//_______________________________________________________________________________
//_______________________________________________________________________________
namespace mpi
{
//_______________________________________________________________________________
enum
{
    NATIVE_MPI_TYPE,
    CLASS_MPI_TYPE,
    SPECIAL_MPI_TYPE,
};
//_______________________________________________________________________________
template<typename T>
struct mpi_type_info
{
    enum { result = meta::is_visitable<T>::result ? CLASS_MPI_TYPE : SPECIAL_MPI_TYPE };
};
//.............................................................................
template<>
struct mpi_type_info<bool>//TODO review: boost::MPI, 'build_mpi_datatype_for_bool'
{
    enum { result = NATIVE_MPI_TYPE };
    //:bad idea becoz MPI_* are pointers in lam
    //:enum { mpi_datatype = MPI_BYTE };
    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_BYTE;
    }
};
//.............................................................................
template<>
struct mpi_type_info<char>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_CHAR;
    }
};
//.............................................................................
template<>
struct mpi_type_info<unsigned char>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_UNSIGNED_CHAR;
    }
};
//.............................................................................
template<>
struct mpi_type_info<short>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_SHORT;
    }
};
//.............................................................................
template<>
struct mpi_type_info<unsigned short>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_UNSIGNED_SHORT;
    }
};
//.............................................................................
template<>
struct mpi_type_info<int>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_INT;
    }
};
//.............................................................................
template<>
struct mpi_type_info<unsigned int>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_UNSIGNED;
    }
};
//.............................................................................
template<>
struct mpi_type_info<long>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_LONG;
    }
};
//.............................................................................
template<>
struct mpi_type_info<unsigned long>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_UNSIGNED_LONG;
    }
};
//.............................................................................
template<>
struct mpi_type_info<float>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_FLOAT;
    }
};
//.............................................................................
template<>
struct mpi_type_info<double>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_DOUBLE;
    }
};
//_______________________________________________________________________________
//_______________________________________________________________________________
//TODO review: where long != int64_t?
#if defined(_WIN32)

#if (defined(MPI_VERSION) && MPI_VERSION >= 2) || defined(MPI_LONG_LONG_INT)
template<>
struct mpi_type_info<int64_t>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_LONG_LONG_INT;
    }
};
#endif
//_______________________________________________________________________________
#if (defined(MPI_VERSION) && MPI_VERSION >= 2) || defined(MPI_UNSIGNED_LONG_LONG)
template<>
struct mpi_type_info<uint64_t>
{
    enum { result = NATIVE_MPI_TYPE };

    static MPI_Datatype get_MPI_Datatype()
    {
        return MPI_UNSIGNED_LONG_LONG;
    }
};
#endif

#endif
//_______________________________________________________________________________
//_______________________________________________________________________________
struct isend_buffer
{
    MPI_Request request_;
    size_t      cb_;
    union
    {
        void*       pv_;
        uint64_t    dummy_;
    };
};
//_______________________________________________________________________________
//_______________________________________________________________________________
class implementation
{
    DISALLOW_COPY_AND_ASSIGN(implementation);
public:
    typedef std::map<std::type_info const*, MPI_Datatype, meta::type_info_less> TTypeMap;
    static TTypeMap s_map;
    typedef std::vector<char> TBuffer;
    static TBuffer s_buffer;
    typedef std::vector<isend_buffer> TIsendBuffer;
    static TIsendBuffer s_isend_list;
public:
    explicit implementation(int& argc, char**& argv)
    {
        ::MPI_Init(&argc, &argv);
    }
//.............................................................................
    ~implementation()
    {
        for (TTypeMap::iterator it = s_map.begin(); it != s_map.end(); ++it)
            ::MPI_Type_free(&(it->second));
        attach_Buffer(0);
        ::MPI_Finalize();
    }
//.............................................................................
    static double time()
    {
        return ::MPI_Wtime();
    }
//.............................................................................
    static int Comm_rank(MPI_Comm comm)
    {
        int myid;
        ::MPI_Comm_rank(comm, &myid);
        return myid;
    }
//.............................................................................
    static int Comm_size(MPI_Comm comm)
    {
        int numprocs;
        ::MPI_Comm_size(comm, &numprocs);
        return numprocs;
    }
//.............................................................................
    static int barrier(MPI_Comm comm)
    {
        return ::MPI_Barrier(comm);
    }
//.............................................................................
    static int attach_Buffer(int cbSize)
    {
        if (!s_buffer.empty())
        {
            int dummy = 0;
            ::MPI_Buffer_detach(&s_buffer[0], &dummy);
        }
        s_buffer.resize(cbSize);
        if (cbSize > 0)
            return ::MPI_Buffer_attach(&s_buffer[0], cbSize);
        return MPI_SUCCESS;
    }
//.............................................................................
//.............................................................................
    static int Send(void* pv, size_t count, MPI_Datatype type, int dest, int tag, MPI_Comm comm)
    {
#if defined(MPI_TRACE_SEND)
        size_t dw = (size_t)pv;
        std::cout << "MPI_Send(0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << dw
            << ", " << std::setbase(10) << std::setw(8) << std::setfill(' ') << count
            << ", 0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << type
            << ", " << std::setbase(10) << dest << ", ...)" << std::endl;
#endif
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Send(pv, static_cast<int>(count), type, dest, tag, comm);
    }
//.............................................................................
    static int Isend(void* pv, size_t count, MPI_Datatype type, int dest,
        int tag, MPI_Comm comm, MPI_Request* pRequest)
    {
#if defined(MPI_TRACE_ISEND)
        size_t dw = (size_t)pv;
        std::cout << "MPI_Isend(0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << dw
            << ", " << std::setbase(10) << std::setw(8) << std::setfill(' ') << count
            << ", 0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << type
            << ", " << std::setbase(10) << dest << ", ...)" << std::endl;
#endif
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Isend(pv, static_cast<int>(count), type, dest, tag, comm, pRequest);
    }
//.............................................................................
    static int Ibsend(void* pv, size_t count, MPI_Datatype type, int dest,
        int tag, MPI_Comm comm, MPI_Request* pRequest)
    {
#if defined(MPI_TRACE_IBSEND)
        size_t dw = (size_t)pv;
        std::cout << "MPI_Ibsend(0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << dw
            << ", " << std::setbase(10) << std::setw(8) << std::setfill(' ') << count
            << ", 0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << type
            << ", " << std::setbase(10) << dest << ", ...)" << std::endl;
#endif
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Ibsend(pv, static_cast<int>(count), type, dest, tag, comm, pRequest);
    }
//.............................................................................
//.............................................................................
    static int Recv(void* pv, size_t count, MPI_Datatype type, int src, int tag, MPI_Comm comm,
        MPI_Status& status)
    {
#if defined(MPI_TRACE_RECV)
        size_t dw = (size_t)pv;
        std::cout << "MPI_Recv(0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << dw
            << ", " << std::setbase(10) << std::setw(8) << std::setfill(' ') << count
            << ", 0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << type
            << ", " << std::setbase(10) << src << ", ...)" << std::endl;
#endif
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Recv(pv, static_cast<int>(count), type, src, tag, comm, &status);
    }
//.............................................................................
    static int Bcast(void* pv, size_t count, MPI_Datatype type, int tag, MPI_Comm comm)
    {
#if defined(MPI_TRACE_BCAST)
        size_t dw = (size_t)pv;
        std::cout << "MPI_Bcast(0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << dw
            << ", " << std::setbase(10) << std::setw(8) << std::setfill(' ') << count
            << ", 0x" << std::setbase(16) << std::setw(8) << std::setfill('0') << type
            << ", ...)" << std::endl;
#endif
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Bcast(pv, static_cast<int>(count), type, tag, comm);
    }
//.............................................................................
    static int Iprobe(int source, int tag, MPI_Comm comm, int& flag, 
               MPI_Status& status)
    {
        return ::MPI_Iprobe(source, tag, comm, &flag, &status);
    }
//.............................................................................
//.............................................................................
    static int Reduce(void* src, void* dst, int nCount, MPI_Datatype type, MPI_Op nOp,
        int nRoot, MPI_Comm comm)
    {
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Reduce(src, dst, nCount, type, nOp, nRoot, comm);
    }
//.............................................................................
    static int Allreduce(void* src, void* dst, int nCount, MPI_Datatype type, MPI_Op nOp,
        MPI_Comm comm)
    {
        ASSERT_(type != MPI_DATATYPE_NULL);
        return ::MPI_Allreduce(src, dst, nCount, type, nOp, comm);
    }
//.............................................................................
    static int Gather(void* src, int sCount, MPI_Datatype sType,
        void* dst, int dCount, MPI_Datatype dType,
        int nRoot, MPI_Comm comm)
    {
        ASSERT_(sType != MPI_DATATYPE_NULL);
        ASSERT_(dType != MPI_DATATYPE_NULL);
        return ::MPI_Gather(src, sCount, sType, dst, dCount, dType, nRoot, comm);
    }
//.............................................................................
    static int Allgather(void* src, int sCount, MPI_Datatype sType,
        void* dst, int dCount, MPI_Datatype dType,
        MPI_Comm comm)
    {
        ASSERT_(sType != MPI_DATATYPE_NULL);
        ASSERT_(dType != MPI_DATATYPE_NULL);
        return ::MPI_Allgather(src, sCount, sType, dst, dCount, dType, comm);
    }
//.............................................................................
//.............................................................................
    static int Test(MPI_Request* prq, int* fComplete, MPI_Status* pst)
    {
        return ::MPI_Test(prq, fComplete, pst);
    }
//.............................................................................
    static int Wait(MPI_Request* prq, MPI_Status* pst)
    {
        return ::MPI_Wait(prq, pst);
    }
//.............................................................................
//.............................................................................
//.............................................................................
//.............................................................................
    static isend_buffer& alloc_Isend(void* pt, size_t cb);
//.............................................................................
    static void test_Isend();
//.............................................................................
//.............................................................................
    template<typename T>
    static MPI_Datatype register_Type(T& t, std::type_info const& ti);//:below
//.............................................................................
    template<typename T>
    static MPI_Datatype find_Type(T& t)
    {
        std::type_info const& ti = typeid(t);
        TTypeMap::const_iterator pos = s_map.find(&ti);
        if (pos != s_map.end())
            return pos->second;
        return register_Type(t, ti);
    }
//.............................................................................
    template<typename T>
    static MPI_Datatype get_Type(T& t)
    {
        return get_Type<T>(t, meta::int2type<mpi_type_info<T>::result>());
    }
//.............................................................................
    template<typename T>
    static MPI_Datatype get_Type(T& t, meta::int2type<NATIVE_MPI_TYPE>)
    {
        return mpi_type_info<T>::get_MPI_Datatype();
    }
//.............................................................................
    template<typename T>
    static MPI_Datatype get_Type(T& t, meta::int2type<CLASS_MPI_TYPE>)
    {
        return find_Type(t);
    }
//.............................................................................
};
//__________________________________________________________________________________
//_______________________________________________________________________________
namespace hidden
{
//_______________________________________________________________________________
template<class V>
class mpi_ptr_visitor:
    public meta::visitor< mpi_ptr_visitor<V> >
{
public:
    typedef meta::visitor< mpi_ptr_visitor<V> > TVisitor;
    using TVisitor::self;
    V& v_;
//.............................................................................
    explicit mpi_ptr_visitor(V& v): v_(v)
    {}
//.............................................................................
    template<typename T, typename D>
    void visit(T& t, char const* szName, D const& d)
    {
        visit2(t, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T>
    void visit2(T& t, meta::int2type<SPECIAL_MPI_TYPE> it)
    {
        v_.visit2(t, it);
    }
    template<typename T>
    void visit2(T& t, meta::int2type<NATIVE_MPI_TYPE>)
    {
        //:skip
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const& d)
    {
        visit_Class2(t, szName, d, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T, typename D>
    void visit_Class2(T& t, char const* szName, D const& d, meta::int2type<CLASS_MPI_TYPE>)
    {
        TVisitor::visit_Class(t, szName, d);
    }
    template<typename T, typename D>
    void visit_Class2(T& t, char const* szName, D const& d, meta::int2type<NATIVE_MPI_TYPE>)
    {
        //:skip
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        visit_Array2(pt, N, szName, d, meta::int2type<mpi_type_info<T>::result>());
    }
//.............................................................................
    template<typename T, typename D, int M>
    void visit_Array2(T* pt, size_t N, char const* szName, D const& d, meta::int2type<M>)
    {
        TVisitor::visit_Array(pt, N, szName, d);
    }
    template<typename T, typename D>
    void visit_Array2(T* pt, size_t N, char const* szName, D const& d, meta::int2type<NATIVE_MPI_TYPE>)
    {
        //:skip
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {
        d.accept(pt, v_);
    }
};
//_______________________________________________________________________________
template<class M>
class mpi_visitor:
    public meta::visitor<M>
{
public:
    typedef meta::visitor< M > TVisitor;
    using TVisitor::self;
//.............................................................................
    template<typename T, typename D>
    void visit(T& t, char const* szName, D const& d)
    {
        visit2(t, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T>
    void visit2(T& t, meta::int2type<SPECIAL_MPI_TYPE>)
    {
        transmit_handler<T, M>::visit(t, *self());
    }
    template<typename T>
    void visit2(T& t, meta::int2type<NATIVE_MPI_TYPE>)
    {
        self()->invoke(&t, 1, mpi_type_info<T>::get_MPI_Datatype());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const& d)
    {//:visit self!?
        visit_Class2(t, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T>
    void visit_Class2(T& t, meta::int2type<CLASS_MPI_TYPE>)
    {
        MPI_Datatype mt = implementation::get_Type(t);
        if (MPI_DATATYPE_NULL != mt)
            self()->invoke(&t, 1, mt);

        mpi_ptr_visitor< mpi_visitor<M> > sv(*self());
        t.accept(sv);
    }
    template<typename T>
    void visit_Class2(T& t, meta::int2type<NATIVE_MPI_TYPE>)
    {
        self()->invoke(&t, 1, mpi_type_info<T>::get_MPI_Datatype());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        visit_Array2(pt, N, szName, d, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T, typename D>
    void visit_Array2(T* pt, size_t N, char const* szName, D const& d, meta::int2type<CLASS_MPI_TYPE> it)
    {
        MPI_Datatype mt = implementation::get_Type(*pt, it);
        if (MPI_DATATYPE_NULL != mt)
            self()->invoke(pt, (int)N, mt);

        mpi_ptr_visitor< mpi_visitor<M> > sv(*self());
        sv.visit_Array2(pt, N, szName, d, it);
    }
    template<typename T, typename D>
    void visit_Array2(T* pt, size_t N, char const* szName, D const& d, meta::int2type<SPECIAL_MPI_TYPE> it)
    {
        mpi_ptr_visitor< mpi_visitor<M> > sv(*self());
        sv.visit_Array2(pt, N, szName, d, it);
    }
    template<typename T, typename D>
    void visit_Array2(T* pt, size_t N, char const* szName, D const& d, meta::int2type<NATIVE_MPI_TYPE>)
    {
        self()->invoke(pt, (int)N, mpi_type_info<T>::get_MPI_Datatype());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {
        COMPILETIME_CHECK(0, bad_call);
    }
};
//_______________________________________________________________________________
//_______________________________________________________________________________
template<class C>
struct mpi_type_enum:
    public meta::visitor< mpi_type_enum<C> >
{
    void const*                 pv_;
    std::vector<int>            arr_len_;
    std::vector<MPI_Aint>       arr_offs_;
    std::vector<MPI_Datatype>   arr_type_;
//.............................................................................
    using meta::visitor< mpi_type_enum<C> >::self;
//.............................................................................
    explicit mpi_type_enum():
        pv_(NULL)
    {}
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        ASSERT_(pv_);
        ASSERT_(pv_ <= pt);
        if (MPI_DATATYPE_NULL == result || !count)
            return;
        arr_type_.push_back(result);
        arr_len_.push_back(static_cast<int>(count));
        arr_offs_.push_back((MPI_Aint)((char const*)pt - (char const*)pv_));
    }
//.............................................................................
    template<typename T, typename D>
    void visit(T& t, char const* szName, D const& d)
    {
        visit2(t, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T>
    void visit2(T& t, meta::int2type<SPECIAL_MPI_TYPE>)
    {
        //:skip
    }
    template<typename T>
    void visit2(T& t, meta::int2type<NATIVE_MPI_TYPE>)
    {
        self()->invoke(&t, 1, mpi_type_info<T>::get_MPI_Datatype());
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const& d)
    {
        visit_Class2(t, meta::int2type<meta::is_same_type<C,T>::result>());
    }
//.............................................................................
    template<typename T>
    void visit_Class2(T& t, meta::int2type<1>)
    {//:visit self
        ASSERT_(!pv_);
        pv_ = &t;
        t.accept(*self());
        if (!arr_type_.empty())
        {
            arr_type_.push_back(MPI_UB);
            arr_len_.push_back(1);
            arr_offs_.push_back(sizeof(T));
        }
    }
    template<typename T>
    void visit_Class2(T& t, meta::int2type<0>)
    {//:visit member class
        self()->invoke(&t, 1, implementation::get_Type(t));//:recursive
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        visit_Array2(pt, N, meta::int2type<mpi_type_info<T>::result>());
    }
    template<typename T>
    void visit_Array2(T* pt, size_t N, meta::int2type<CLASS_MPI_TYPE> it)
    {
        self()->invoke(pt, (int)N, implementation::get_Type(*pt, it));
    }
    template<typename T>
    void visit_Array2(T* pt, size_t N, meta::int2type<SPECIAL_MPI_TYPE> it)
    {
        //:skip
    }
    template<typename T>
    void visit_Array2(T* pt, size_t N, meta::int2type<NATIVE_MPI_TYPE> it)
    {
        self()->invoke(pt, (int)N, implementation::get_Type(*pt, it));
    }
//.............................................................................
    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {
        //:skip
    }
//.............................................................................
};
//_______________________________________________________________________________
}
//_______________________________________________________________________________
template<typename T>
MPI_Datatype implementation::register_Type(T& t, std::type_info const& ti)
{
    hidden::mpi_type_enum<T> me;
    me(t);
    int c = (int)me.arr_len_.size();
    MPI_Datatype type = MPI_DATATYPE_NULL;
    if (c > 0)
    {
        int res = ::MPI_Type_struct(c, &me.arr_len_[0], &me.arr_offs_[0], &me.arr_type_[0], &type);
        VERIFY_(MPI_SUCCESS == res);
        ////MPI_Aint lb, cb;
        ////::MPI_Type_get_extent(type, &lb, &cb);
        ////std::cout << "lb=" << lb << ", cb=" << cb << std::endl;
        s_map[&ti] = type;
        res = ::MPI_Type_commit(&type);
        VERIFY_(MPI_SUCCESS == res);
    }
#if defined(MPI_TRACE_TYPE)
    std::cout << "register mpi type: 0x" << setbase(16) << type << "='" << ti.name() << "'" << std::endl;
#endif
    return type;

}
//_______________________________________________________________________________
//class isend;
//class ibsend;
//class send;
//class recv;
//_______________________________________________________________________________
class communicator
{
protected:
    MPI_Comm comm_;
public:
    explicit communicator(): comm_(MPI_COMM_WORLD)
    {}
//.............................................................................
//.............................................................................
    operator MPI_Comm() const
    {
        return comm_;
    }
//.............................................................................
    int rank() const
    {
        return implementation::Comm_rank(comm_);
    }
//.............................................................................
    int size() const
    {
        return implementation::Comm_size(comm_);
    }
//.............................................................................
    int barrier() const
    {
        return implementation::barrier(comm_);
    }
//.............................................................................
    bool is_Data_Available(int src, int tag, int& last_error) const
    {
        MPI_Status status;
        int pending = 0;
        last_error = implementation::Iprobe(src, tag, comm_, pending, status);
        ASSERT_(MPI_SUCCESS == last_error);
        return pending != 0;
    }
//.............................................................................
    void abort(int err = -1) const
    {
        ::MPI_Abort(comm_, err);
    }
//.............................................................................
    template<typename T>
    int reduce(T const& t, MPI_Op op, int root) const
    {
        ASSERT_(root != rank());
        T& tmp = const_cast<T&>(t);
        COMPILETIME_CHECK((int)mpi_type_info<T>::result == (int)NATIVE_MPI_TYPE, only_native_types_supported);
        MPI_Datatype mt = implementation::get_Type(tmp);
        return implementation::Reduce(&tmp, NULL, 1, mt, op, root, comm_);
    }
//.............................................................................
    template<typename T>
    int reduce(T const& in, T& out, MPI_Op op, int root) const
    {
        ASSERT_(root == rank());
        T& tmp = const_cast<T&>(in);
        COMPILETIME_CHECK((int)mpi_type_info<T>::result == (int)NATIVE_MPI_TYPE, only_native_types_supported);
        MPI_Datatype mt = implementation::get_Type(tmp);
        return implementation::Reduce(&tmp, &out, 1, mt, op, root, comm_);
    }
//.............................................................................
    template<typename T>
    int all_reduce(T const& in, T& out, MPI_Op op) const
    {
        T& tmp = const_cast<T&>(in);
        COMPILETIME_CHECK((int)mpi_type_info<T>::result == (int)NATIVE_MPI_TYPE, only_native_types_supported);
        MPI_Datatype mt = implementation::get_Type(tmp);
        return implementation::Allreduce(&tmp, &out, 1, mt, op, comm_);
    }
//.............................................................................
//.............................................................................
//.............................................................................
    template<typename T>
    int gather(T* in, int count, int root) const
    {
        ASSERT_(root != rank());
        MPI_Datatype mt = implementation::get_Type(*in);
        return implementation::Gather(in, count, mt, in, count, mt, root, comm_);
    }
//.............................................................................
    template<typename T>
    int gather(T* in, int count, T* out, int root) const
    {
        ASSERT_(root == rank());
        MPI_Datatype mt = implementation::get_Type(*in);
        return implementation::Gather(in, count, mt, out, count, mt, root, comm_);
    }
//.............................................................................
    template<typename T>
    int all_gather(T* in, int count, T* out) const
    {
        MPI_Datatype mt = implementation::get_Type(*in);
        return implementation::Allgather(in, count, mt, out, count, mt, comm_);
    }
//.............................................................................
//.............................................................................
    static void test_Isend()
    {
        implementation::test_Isend();
    }
//.............................................................................
};
//_______________________________________________________________________________
//_______________________________________________________________________________
//_______________________________________________________________________________
class bcast:
    public hidden::mpi_visitor<bcast>
{
    int root_;
    communicator& comm_;
public:
    int last_error_;
    enum { transmit_tag = BCAST_TAG };
public:
    explicit bcast(int root, communicator& comm): root_(root), comm_(comm),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    int get_Root() const { return root_; }
//.............................................................................
    bool is_Root()
    {
        return comm_.rank() == root_;
    }
//.............................................................................
    template<typename T>
    bcast& operator >>(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    bcast& operator <<(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        last_error_ = implementation::Bcast(pt, count, result, root_, comm_);
        ASSERT_(MPI_SUCCESS == last_error_);
    }
};
//_______________________________________________________________________________
class send:
    public hidden::mpi_visitor<send>
{
    int dst_;
    communicator& comm_;
    int tag_;
public:
    int last_error_;
    enum { transmit_tag = SEND_TAG };
public:
    explicit send(int dest, communicator& comm, int tag = 0): dst_(dest),
        comm_(comm),
        tag_(tag),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    template<typename T>
    send& operator <<(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        last_error_ = implementation::Send(pt, count, result, dst_, tag_, comm_);
        ASSERT_(MPI_SUCCESS == last_error_);
    }
};
//_______________________________________________________________________________
class isend:
    public hidden::mpi_visitor<isend>
{
    int dst_;
    communicator& comm_;
    int tag_;
public:
    MPI_Status status_;
    MPI_Request request_;
    int last_error_;
    enum { transmit_tag = SEND_TAG };
public:
    explicit isend(int dest, communicator& comm, int tag = 0): dst_(dest),
        comm_(comm),
        status_(),
        request_(),
        tag_(tag),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    template<typename T>
    isend& operator <<(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        last_error_ = implementation::Isend(pt, count, result, dst_, tag_, comm_, &request_);
        ASSERT_(MPI_SUCCESS == last_error_);
        //test()
    }
//.............................................................................
    int test(int& fComplete)
    {
        return implementation::Test(&request_, &fComplete, &status_);
    }
//.............................................................................
    int wait()
    {
        return implementation::Wait(&request_, &status_);
    }
};
//_______________________________________________________________________________
class ibsend:
    public hidden::mpi_visitor<ibsend>
{
    int dst_;
    communicator& comm_;
    int tag_;
public:
    int last_error_;
    enum { transmit_tag = SEND_TAG };
public:
    explicit ibsend(int dest, communicator& comm, int tag = 0): dst_(dest),
        comm_(comm),
        tag_(tag),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    template<typename T>
    ibsend& operator <<(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        size_t cb = count * sizeof(T);
        isend_buffer& b = implementation::alloc_Isend(pt, cb);
        void* pv = (cb <= sizeof(b.dummy_)) ? &b.dummy_ : b.pv_;
        last_error_ = implementation::Isend(pv, count, result, dst_, tag_, comm_, &b.request_);
        ASSERT_(MPI_SUCCESS == last_error_);
    }
//.............................................................................
};
//_______________________________________________________________________________
/*
class ibsend:
    public hidden::mpi_visitor<ibsend>
{
    int dst_;
    communicator& comm_;
    int tag_;
public:
    int last_error_;
    enum { transmit_tag = SEND_TAG };
public:
    explicit ibsend(int dest, communicator& comm, int tag = 0): dst_(dest),
        comm_(comm),
        tag_(tag),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    template<typename T>
    ibsend& operator <<(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        MPI_Request r;
        last_error_ = implementation::Ibsend(pt, count, result, dst_, tag_, comm_, &r);
        ASSERT_(MPI_SUCCESS == last_error_);
    }
};
*/
//_______________________________________________________________________________
//_______________________________________________________________________________
class recv:
    public hidden::mpi_visitor<recv>
{
    int src_;
    communicator& comm_;
    int tag_;
public:
    MPI_Status status_;
    int last_error_;
    enum { transmit_tag = RECV_TAG };
public:
    explicit recv(int src, communicator& comm, int tag = 0): src_(src),
        comm_(comm),
        tag_(tag),
        last_error_(MPI_SUCCESS)
    {}
//.............................................................................
    template<typename T>
    recv& operator >>(T& t)
    {
        visit_T(t);
        return *this;
    }
//.............................................................................
    template<typename T>
    void invoke(T* pt, size_t count, MPI_Datatype result)
    {
        last_error_ = implementation::Recv(pt, count, result, src_, tag_, comm_, status_);
        ASSERT_(MPI_SUCCESS == last_error_);
    }
//.............................................................................
    bool is_Data_Available(int tag = MPI_ANY_TAG)
    {
        return comm_.is_Data_Available(src_, tag, last_error_);
    }
};
//_______________________________________________________________________________
}
//__________________________________________________________________________________
//__________________________________________________________________________________
//EOF!
